/*
 * mdb - A memory database
 * author	: calvin
 * email	: calvinwilliams@163.com
 *
 * Licensed under the LGPL v2.1, see the file LICENSE in base directory.
 */

#ifndef _H_IN_
#define _H_IN_

#ifdef __cplusplus
extern "C" {
#endif

#include "list.h"
#include "rbtree.h"

#include "mdb.h"

#define MDB_TRACE		0

#ifndef STRCMP
#define STRCMP(_a_,_C_,_b_) ( strcmp(_a_,_b_) _C_ 0 )
#define STRNCMP(_a_,_C_,_b_,_n_) ( strncmp(_a_,_b_,_n_) _C_ 0 )
#endif

#ifndef MEMCMP
#define MEMCMP(_a_,_C_,_b_,_n_) ( memcmp(_a_,_b_,_n_) _C_ 0 )
#endif

#if MDB_TRACE
#define TRACE_PRINTF(...)	{ \
					struct timeval	tv ; \
					struct tm	tm ; \
					char		tbuf[10+1+8+1]; \
					gettimeofday( & tv , NULL ); \
					localtime_r( & (tv.tv_sec) , & tm ); \
					strftime( tbuf , sizeof(tbuf) , "%Y-%m-%d %H:%M:%S" , & tm ); \
					printf( "%s.%06ld | %s:%d:%s | " , tbuf , tv.tv_usec , __FILE__ , __LINE__ , __FUNCTION__ ); printf( __VA_ARGS__ ); \
				} \

#else
#define TRACE_PRINTF(...)
#endif

#if MDB_TRACE
#define TRACE_PRINTHEX(...)	printhex( __VA_ARGS__ )
#else
#define TRACE_PRINTHEX(...)
#endif

#if MDB_TRACE
#define TRACE_PRINTEOL()		printf( "\n" );
#else
#define TRACE_PRINTEOL()
#endif

#define MDB_TABLES_HASH_SIZE	32

#define MDB_PARSE_INDEX_FIELD_ARRAY_MAXCOUNT	16
#define MDB_PARSE_SELECT_FIELD_ARRAY_MAXCOUNT	16
#define MDB_PARSE_SET_FIELD_ARRAY_MAXCOUNT	16
#define MDB_PARSE_WHERE_FIELD_ARRAY_MAXCOUNT	16

#define MDB_RECORD_NO_INDEX_NAME		"(record_no_index)"
#define MDB_RECORD_NO_FIELD_NAME		"(record_no)"

#define MDB_DATAPAGE_CACHE_RESERVED_COUNT	1

extern __thread int		tls_last_errno ;

/*
 * Util
 */

struct MemField ;

uint32_t Hash( unsigned char *str , size_t len );
void printhex( char *buf , size_t buf_len );

int ConvertArgsToArray( va_list valist , struct MemField *field_array , size_t field_array_size , size_t *p_field_array_count );

/*
 * MemField
 */

struct MemField
{
	struct list_head	field_list_node ;
	
	char			*field_name ;
	int			field_offset ;
	size_t			field_size ;
} ;

/*
 * DataPage
 */

struct MemDataPage
{
	struct list_head	datapage_list_node ;
	
	void			*subject_to_object ;
	size_t			*p_datapage_count ;
	
	size_t			dataunit_size ;
	size_t			dataunit_prealloc_count ;
	size_t			dataunit_used_count ;
	uint64_t		dataunit_used_bitmap ;
	char			dataunit_array_base[0] ;
} ;

struct MemDataPage *CreateDataPage( void *subject_to_object , size_t dataunit_size , struct list_head *datapage_list , size_t *p_datapage_count );
void DestroyDataPage( struct MemDataPage *datapage );
void FreeDataPageDirectly( struct list_head *datapage_list );

size_t GetDataUnitSize( struct MemDataPage *datapage );
size_t GetPreAllocCount( struct MemDataPage *datapage );
size_t GetUsedCount( struct MemDataPage *datapage );
unsigned char IsDataPageEmpty( struct MemDataPage *datapage );
unsigned char IsDataPageFull( struct MemDataPage *datapage );

char *GetUnusedDataUnit( struct MemDataPage *datapage );
void SetDataUnitUnused( struct MemDataPage *datapage , char *dataunit );

/*
 * Transaction
 */

struct MemTransaction
{
	uint64_t		txn_no ;
	/*
	struct rb_node		txn_dataunit_tree_node ;
	struct MemDataPage	*txn_datapage ;
	
	struct MemDatabase	*db ;
	*/
	
	struct list_head	undo_list ; /* struct MemUndoData */
	struct MemUndoData	*executed_undo ;
} ;

/*
struct MemTransaction *GetUnusedTransactionData( struct MemDatabase *db );
void SetTransactionDataUnused( struct MemTransaction *txn_data );
*/

int CreateCurrentTransaction( struct MemDatabase *db );
int ExecuteCurrentTransaction( struct MemDatabase *db );
int CancelCurrentTransaction( struct MemDatabase *db );
int DestroyCurrentTransaction( struct MemDatabase *db );

/*
 * Undo
 */

#define MDB_UNDO_OPERATION_ADD_RECORD			11
#define MDB_UNDO_OPERATION_BACKUP_AND_REMOVE_RECORD	12
#define MDB_UNDO_OPERATION_BACKUP_AND_MODIFY_RECORD	13
#define MDB_UNDO_OPERATION_ADD_INDEX_ON_RECORD		21
#define MDB_UNDO_OPERATION_REMOVE_INDEX_ON_RECORD	22

struct MemUndoData
{
	struct list_head	undo_list_node ;
	struct MemDataPage	*undo_datapage ;
	
	uint16_t		operation ;
	struct MemTable		*table ;
	struct MemIndex		*index ;
	char			*record_dataunit ;
	char			*backup_record_dataunit ;
	char			*new_record_dataunit ;
	struct MemUndoData	*undo_data ;
	char			*index_dataunit ;
} ;

struct MemUndoData *GetUnusedUndoData( struct MemDatabase *db );
void SetUndoDataUnused( struct MemUndoData *undo_data );

int AppendInsertUndoDataToCurrentTransaction( struct MemTable *table , char *record_dataunit );
int AppendUpdateUndoDataToCurrentTransaction( struct MemTable *table , char *record_dataunit , char *set_record );
int AppendDeleteUndoDataToCurrentTransaction( struct MemTable *table , char *record_dataunit );

/*
 * Database
 */

struct MemDatabase
{
	char			db_name[ MDB_DATABASE_NAME_MAX_LENGTH + 1 ] ;
	size_t			db_name_len ;
	
	struct hlist_head	*tables_hash ;
	uint32_t		tables_hash_size ;			
	
	uint64_t		max_txn_no ;
	
#if 0
	struct list_head	txn_datapage_list ; /* struct MemDataPage */
	size_t			txn_datapage_count ;
	struct rb_root		txn_dataunit_tree ; /* struct MemTransaction */
#endif
	unsigned char		is_in_txn ;
	struct MemTransaction	txn ;
	
	struct list_head	undo_datapage_list ; /* struct MemDataPage */
	size_t			undo_datapage_count ;
} ;

int CreateMemTablesHash( struct MemDatabase *db );
int DestroyMemTablesHash( struct MemDatabase *db );

int AddMemTablesHashNodeDirectly( struct MemDatabase *db , struct MemTable *table );
int RemoveMemTablesHashNode( struct MemDatabase *db , char *table_name );
int RemoveMemTablesHashNodeDirectly( struct MemDatabase *db , struct MemTable *table );
struct MemTable *QueryMemTablesHashNode( struct MemDatabase *db , char *table_name );

/*
 * Table
 */

struct MemTable
{
	char			table_name[ MDB_TABLE_NAME_MAX_LENGTH + 1 ] ;
	size_t			table_name_len ;
	struct hlist_node	table_hash_node ;
	
	struct MemDatabase	*db ;
	
	size_t			record_size ;
	uint64_t		max_record_no ;
	
	struct list_head	index_list ; /* struct MemIndex */
	struct MemIndex		*record_no_index ;
	
	struct list_head	record_datapage_list ; /* struct MemDataPage */
	size_t			record_datapage_count ;
	
	struct list_head	uncommit_record_datapage_list ; /* struct MemDataPage */
	size_t			uncommit_record_datapage_count ;
	
	struct MemField		auto_increment_field ;
	union
	{
		uint8_t		uint8 ;
		uint16_t	uint16 ;
		uint32_t	uint32 ;
		uint64_t	uint64 ;
	} auto_increment_value ;
} ;

void SetAutoIncrementFieldValue( struct MemTable *table , char *record_data );

/*
 * Index
 */

struct MemIndex
{
	char			index_name[ MDB_INDEX_NAME_MAX_LENGTH + 1 ] ;
	size_t			index_name_len ;
	unsigned char		is_unique ;
	
	struct MemTable		*table ;
	
	struct list_head	index_list_node ; /* struct MemIndex */
	
	struct MemField		index_field_array[ MDB_PARSE_INDEX_FIELD_ARRAY_MAXCOUNT ] ;
	size_t			index_field_array_count ;
	
	struct list_head	index_datapage_list ; /* MemDataPage */
	size_t			index_datapage_count ;
	struct rb_root		index_dataunit_tree ;
} ;

struct MemIndexDataUnit
{
	struct rb_node		index_dataunit_tree_node ;
	struct MemDataPage	*index_datapage ;
	char			*record_dataunit ;
} ;

struct MemIndex *QueryExecuteSchedule( struct MemTable *table , struct MemField *where_field_array , size_t where_field_array_count );

int AddIndexDataOnRecord( struct MemTable *table , char *record_dataunit , struct MemIndex *index );
int DeleteIndexDataOnRecord( struct MemTable *table , char *record_dataunit , struct MemIndex *index );

int AddIndexDataUnitTreeNode( char *add_record_dataunit , struct MemIndex *index , struct MemIndexDataUnit *dataunit );
struct MemIndexDataUnit *QueryIndexDataUnitTreeNode( char *record_dataunit , struct MemIndex *index , struct MemField *field_array , size_t field_array_count );

struct MemIndexDataUnit *GetPrevIndexDataUnit( struct MemIndexDataUnit *index_dataunit );
struct MemIndexDataUnit *GetNextIndexDataUnit( struct MemIndexDataUnit *index_dataunit );

/*
 * Record
 */

struct MemRecordDataUnitHead
{
	struct MemDataPage	*record_datapage ;
	
	uint64_t		insert_txn_id ; /* can see on current txn_id >= insert_txn_id */
	uint64_t		delete_txn_id ; /* can see on current txn_id < delete_txn_id */
	
	uint64_t		record_no ;
} ;

#define MDB_RECORD_DATAUNIT_TO_DATA(_record_dataunit_)	( (char*)(_record_dataunit_)+sizeof(struct MemRecordDataUnitHead) )
#define MDB_RECORD_DATA_TO_DATAUNIT(_record_data_)	( (char*)(_record_data_)-sizeof(struct MemRecordDataUnitHead) )
#define MDB_RECORD_DATAUNIT_TO_HEAD(_record_dataunit_)	( (struct MemRecordDataUnitHead*)(_record_dataunit_) )

int TestRecordMatched( char *record_data , struct MemField *where_field_array , size_t where_field_array_count , void *record_buffer );
char *QueryFirstRecordExisted( struct MemTable *table );
char *QueryNextRecordExisted( struct MemTable *table , char *begin_record_dataunit );
char *QueryFirstRecordWithoutIndex( struct MemTable *table , void *where_record_data , struct MemField *where_field_array , size_t where_field_array_count );
char *QueryNextRecordWithoutIndex( struct MemTable *table , char *begin_record_dataunit , void *where_record , struct MemField *where_field_array , size_t where_field_array_count );
void CopyRecordFields( char *record_data , struct MemField *select_field_array , size_t select_field_array_count , char *select_record );

char *GetUnusedRecordDataUnit( struct MemTable *table );
void SetRecordDataUnitUnused( char *record_dataunit );

char *GetUnusedUncommitRecordDataUnit( struct MemTable *table );
void SetUncommitRecordDataUnitUnused( char *record_dataunit );

#ifdef __cplusplus
}
#endif

#endif

